package org.seed.rpc.serverstub;

import io.netty.channel.ChannelFutureListener;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.SimpleChannelInboundHandler;
import lombok.extern.slf4j.Slf4j;
import org.seed.rpc.common.io.RpcRequest;
import org.seed.rpc.common.io.RpcResponse;
import org.springframework.cglib.reflect.FastClass;
import org.springframework.cglib.reflect.FastMethod;

import java.util.Map;


/**
 * 调用目标bean,完成server端的执行
 */
@Slf4j
public class RpcServerHandler extends SimpleChannelInboundHandler<RpcRequest> {

    private final Map<String, Object> handlerMap;

    public RpcServerHandler(Map<String, Object> handlerMap) {
        this.handlerMap = handlerMap;
    }

    @Override
    public void channelRead0(final ChannelHandlerContext handlerContext, RpcRequest request) throws Exception {
        // 创建并初始化 RPC 响应对象
        RpcResponse response = new RpcResponse();
        response.setRequestId(request.getRequestId());
        try {
            Object result = handle(request);
            response.setResult(result);
        } catch (Exception e) {
            log.error("handle result failure", e);
            response.setException(e);
        }
        // 写入 RPC 响应对象并自动关闭连接
        handlerContext.writeAndFlush(response).addListener(ChannelFutureListener.CLOSE);
    }

    private Object handle(RpcRequest request) throws Exception {
        // 获取服务对象
        String interfaceName = request.getInterfaceName();
        Object serviceBean = handlerMap.get(interfaceName);
        if (serviceBean == null) {
            throw new RuntimeException(String.format("can not find service bean by key: %s", interfaceName));
        }
        // 获取反射调用所需的参数
        Class<?> serviceClass = serviceBean.getClass();
        log.debug("concrete service-class: {}, interface: {}", serviceClass.getName(), interfaceName);
        String methodName = request.getMethodName();
        Class<?>[] parameterTypes = request.getParameterTypes();
        Object[] parameters = request.getParameters();
        // Java 方式的反射调用
        /**
         * Method method = serviceClass.getMethod(methodName, parameterTypes);
         * method.setAccessible(true);
         * return method.invoke(serviceBean, parameters);
         */
        // 使用 CGLib 执行反射调用
        FastClass serviceFastClass = FastClass.create(serviceClass);
        FastMethod serviceFastMethod = serviceFastClass.getMethod(methodName, parameterTypes);
        Object rst = null;
        try {
            rst = serviceFastMethod.invoke(serviceBean, parameters);
        } catch (Exception e) {
            log.error("reflection method invoke error: {}", e.getMessage());
            e.printStackTrace();
            throw e;
        }
        return rst;
    }

    @Override
    public void exceptionCaught(ChannelHandlerContext handlerContext, Throwable cause) {
        log.error("server caught exception: {}", cause.getMessage());
        cause.printStackTrace();
        handlerContext.close();
    }
}
